package inventorysrvlogic

import (
	"context"
	"errors"

	"gozero-microservices/inventory-service/rpc/internal/model"
	"gozero-microservices/inventory-service/rpc/internal/svc"
	"gozero-microservices/inventory-service/rpc/types/inventory"

	"github.com/zeromicro/go-zero/core/logx"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

type InventoryLogic struct {
	ctx    context.Context
	svcCtx *svc.ServiceContext
	logx.Logger
}

var (
	// 预定义唯一索引重复错误变量
	ErrDuplicate = errors.New("index duplicate")

	// 预定义库存不足错误变量
	ErrInventoryDeficiency = errors.New("inventory deficiency")
)

/**
 * 创建库存逻辑实例
 *
 * @param context.Context ctx ----------------------必传
 * @param *svc.ServiceContext svcCtx ---------------必传
 * @return *InventoryLogic
 * @author huwl
 * @date 2025/10/25 22:18:56
 */
func NewInventoryLogicInstance(ctx context.Context, svcCtx *svc.ServiceContext) *InventoryLogic {
	return &InventoryLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

/**
 * 扣除库存
 *
 * @param []*Inventory Inventories -------------必传,库存信息
 * @return *inventory.Result, error
 * @author huwl
 * @date 2025/10/25 22:18:56
 */
func (l *InventoryLogic) DeductInventory(in *inventory.InventoryRequest) (*inventory.Result, error) {
	if len(in.Inventories) == 0 {
		// 如果使用DTM分布式事务此处的code: codes.Aborted 表示失败
		// 如果不使用DTM分布式事务此处的code: codes.InvalidArgument
		return nil, status.Errorf(codes.Aborted, "库存信息错误")
	}

	goodsSkuIds := []int{}
	for _, inventoryVal := range in.Inventories {
		if inventoryVal.Id <= 0 {
			return nil, status.Errorf(codes.Aborted, "商品skuId错误")
		}
		if inventoryVal.InventoryNum < 0 {
			return nil, status.Errorf(codes.Aborted, "商品sku库存错误")
		}

		goodsSkuIds = append(goodsSkuIds, int(inventoryVal.Id))
	}

	// 屏蔽子事务参数
	if in.Barrier.TransType == "" {
		return nil, status.Errorf(codes.Aborted, "事务类型不能为空")
	}
	if in.Barrier.Gid == "" {
		return nil, status.Errorf(codes.Aborted, "全局事务Id不能为空")
	}
	if in.Barrier.BranchId == "" {
		return nil, status.Errorf(codes.Aborted, "扣除库存子事务Id不能为空")
	}
	if in.Barrier.BarrierId == "" {
		in.Barrier.BarrierId = "deduct-inventory"
	}

	// 事务更新
	err := l.svcCtx.GoodsSkuModel.Conn().TransactCtx(context.Background(), func(ctx context.Context, s sqlx.Session) error {
		// 获取商品SKU信息
		goodsSkus := []*model.MallGoodsSku{}
		err := l.svcCtx.GoodsSkuModel.GetResultsByParams(ctx, &goodsSkus, map[string]interface{}{
			"id IN (?)": goodsSkuIds,
		}, 1, -1, "id desc")
		if err != nil {
			return err
		}

		// 新增子事务屏蔽数据
		_, err = l.svcCtx.BarrierModel.InsertTx(ctx, s, model.MallBarrier{
			TransType: in.Barrier.TransType,
			Gid:       in.Barrier.Gid,
			BranchId:  in.Barrier.BranchId,
			BarrierId: in.Barrier.BarrierId,
			Reason:    in.Barrier.Reason,
		})
		if err != nil {
			// 校验是否唯一索引冲突错误
			b := l.svcCtx.BarrierModel.IsDuplicateEntryError(err)
			if b {
				return ErrDuplicate
			}
			return err
		}

		for _, inventoryValue := range in.Inventories {
			// 计算库存
			var inventoryNum uint8
			for _, goodsSku := range goodsSkus {
				if goodsSku.Id == uint32(inventoryValue.Id) {
					if goodsSku.InventoryNum < uint8(inventoryValue.InventoryNum) {
						return ErrInventoryDeficiency
					}
					inventoryNum = goodsSku.InventoryNum - uint8(inventoryValue.InventoryNum)
					break
				}
			}

			err := l.svcCtx.GoodsSkuModel.EditByPrimaryKeyWithTx(ctx, s, map[string]interface{}{
				"inventory_num": inventoryNum,
			}, inventoryValue.Id)
			if err != nil {
				return err // 返回错误，事务会自动回滚
			}
		}

		return nil
	})
	if err != nil {
		switch err {
		case ErrInventoryDeficiency:
			return nil, status.Errorf(codes.Aborted, "商品库存不足")
		case ErrDuplicate:
			return &inventory.Result{
				Code: 200,
				Msg:  "已扣除库存成功，无需重试",
			}, nil
		}

		return nil, status.Errorf(codes.Aborted, "扣除库存失败")
	}

	return &inventory.Result{
		Code: 200,
		Msg:  "扣除库存成功",
	}, nil
}

/**
 * 还原库存
 *
 * @param []*Inventory Inventories-必传,库存信息
 * @return *inventory.Result, error
 * @author huwl
 * @date 2025/10/25 22:18:56
 */
func (l *InventoryLogic) RevivificationInventory(in *inventory.InventoryRequest) (*inventory.Result, error) {
	if len(in.Inventories) == 0 {
		// 如果使用DTM分布式事务此处的code: codes.Aborted 表示失败
		// 如果不使用DTM分布式事务此处的code: codes.InvalidArgument
		return nil, status.Errorf(codes.Aborted, "库存信息错误")
	}

	goodsSkuIds := []int{}
	for _, inventoryVal := range in.Inventories {
		if inventoryVal.Id <= 0 {
			return nil, status.Errorf(codes.Aborted, "商品skuId错误")
		}
		if inventoryVal.InventoryNum < 0 {
			return nil, status.Errorf(codes.Aborted, "商品数量错误")
		}

		goodsSkuIds = append(goodsSkuIds, int(inventoryVal.Id))
	}

	// 屏蔽子事务参数
	if in.Barrier.TransType == "" {
		return nil, status.Errorf(codes.Aborted, "事务类型不能为空")
	}
	if in.Barrier.Gid == "" {
		return nil, status.Errorf(codes.Aborted, "全局事务Id不能为空")
	}
	if in.Barrier.BranchId == "" {
		return nil, status.Errorf(codes.Aborted, "还原库存子事务Id不能为空")
	}
	if in.Barrier.BarrierId == "" {
		in.Barrier.BarrierId = "recover-inventory"
	}

	// 判断是否允许还原库存
	// 如果未扣除库存则不允许还原库存
	total, err := l.svcCtx.BarrierModel.GetResultsByTotal(l.ctx, map[string]interface{}{
		"gid": in.Barrier.Gid,
	})
	if err != nil {
		return nil, status.Errorf(codes.Aborted, err.Error())
	}
	if total <= 0 {
		return &inventory.Result{
			Code: 200,
			Msg:  "未扣除，无需还原",
		}, nil
	}

	err = l.svcCtx.GoodsSkuModel.Conn().TransactCtx(context.Background(), func(ctx context.Context, s sqlx.Session) error {
		// 获取商品SKU信息
		goodsSkus := []*model.MallGoodsSku{}
		err := l.svcCtx.GoodsSkuModel.GetResultsByParams(ctx, &goodsSkus, map[string]interface{}{
			"id IN (?)": goodsSkuIds,
		}, 1, -1, "id desc")
		if err != nil {
			return err
		}

		// 新增子事务屏蔽数据
		_, err = l.svcCtx.BarrierModel.InsertTx(ctx, s, model.MallBarrier{
			TransType: in.Barrier.TransType,
			Gid:       in.Barrier.Gid,
			BranchId:  in.Barrier.BranchId,
			BarrierId: in.Barrier.BarrierId,
			Reason:    in.Barrier.Reason,
		})
		if err != nil {
			// 校验是否唯一索引冲突错误
			b := l.svcCtx.BarrierModel.IsDuplicateEntryError(err)
			if b {
				return ErrDuplicate
			}

			return err
		}

		for _, inventoryValue := range in.Inventories {
			// 计算库存
			var inventoryNum uint8
			for _, goodsSku := range goodsSkus {
				if goodsSku.Id == uint32(inventoryValue.Id) {
					inventoryNum = goodsSku.InventoryNum + uint8(inventoryValue.InventoryNum)
					break
				}
			}

			// 更新库存
			err = l.svcCtx.GoodsSkuModel.EditByPrimaryKeyWithTx(ctx, s, map[string]interface{}{
				"inventory_num": inventoryNum,
			}, inventoryValue.Id)
			if err != nil {
				return err
			}
		}

		return nil
	})

	if err != nil {
		if err == ErrDuplicate {
			return &inventory.Result{
				Code: 200,
				Msg:  "已还原库存成功，无需重试",
			}, nil
		}
		return nil, status.Errorf(codes.Aborted, "还原库存失败")
	}

	return &inventory.Result{
		Code: 200,
		Msg:  "还原库存成功",
	}, nil
}
